module conv_kernel #(
    parameter N_IH     = 32,
    parameter N_IW     = 32,
    parameter N_ICH    = 16,
    parameter N_OCH    = 16,
    parameter N_OH     = 32,
    parameter N_OW     = 32,
    parameter P_IN     = 4, 
    parameter P_OUT    = 4, 
    parameter P_ICH    = 4, 
    parameter P_OCH    = 4, 
    parameter K        = 3, 
    parameter P        = 1, 
    parameter S        = 1, 
    parameter IA_BIT   = 8, 
    parameter OA_BIT   = 8, 
    parameter W_BIT    = 8, 
    parameter B_BIT    = 32,
    parameter M_BIT    = 8, 
    parameter W_FILE = "",
    parameter B_FILE   = "",
    parameter M_FILE   = ""
) (
    input  wire                    clk,
    input  wire                    rst_n,
    input  wire                    in_valid,
    output wire                    in_ready,
    input  wire [P_IN*IA_BIT-1:0]  in_data,
    output wire                    out_valid,
    input  wire                    out_ready,
    output wire [P_OUT*OA_BIT-1:0] out_data
);
    wire                    s_im2col_in_valid;
    wire                    s_im2col_in_ready;
    wire [P_ICH*IA_BIT-1:0] s_im2col_in_data;
    wire                    s_im2col_valid;
    wire                    s_im2col_ready;
    wire [P_ICH*IA_BIT-1:0] s_im2col_data;
    wire                    s_conv_valid;
    wire                    s_conv_ready;
    wire [P_OCH*B_BIT-1:0]  s_conv_data;
    wire                    s_actv_in_valid;
    wire                    s_actv_in_ready;
    wire [P_OUT*B_BIT-1:0]  s_actv_in_data;

    expand #(
        .WIDTH(P_IN * IA_BIT),
        .N    (P_ICH / P_IN)
    ) u_expand (
        .clk      (clk),
        .rst_n    (rst_n),
        .in_valid (in_valid),
        .in_ready (in_ready),
        .in_data  (in_data),
        .out_valid(s_im2col_in_valid),
        .out_ready(s_im2col_in_ready),
        .out_data (s_im2col_in_data)
    );

    im2col #(
        .P_CH   (P_ICH),
        .N_CH   (N_ICH),
        .N_IH   (N_IH),
        .N_IW   (N_IW),
        .K      (K),
        .P      (P),
        .S      (S),
        .A_BIT  (IA_BIT),
        .PAD_VAL(0)
    ) u_im2col (
        .clk      (clk),
        .rst_n    (rst_n),
        .in_valid (s_im2col_in_valid),
        .in_ready (s_im2col_in_ready),
        .in_data  (s_im2col_in_data),
        .out_valid(s_im2col_valid),
        .out_ready(s_im2col_ready),
        .out_data (s_im2col_data)
    );

    conv #(
        .P_ICH      (P_ICH),
        .P_OCH      (P_OCH),
        .N_ICH      (N_ICH),
        .N_OCH      (N_OCH),
        .K          (K),
        .A_BIT      (IA_BIT),
        .W_BIT      (W_BIT),
        .B_BIT      (B_BIT),
        .N_HW       (N_OH * N_OW),
        .W_FILE(W_FILE)
    ) u_conv (
        .clk      (clk),
        .rst_n    (rst_n),
        .in_data  (s_im2col_data),
        .in_valid (s_im2col_valid),
        .in_ready (s_im2col_ready),
        .out_data (s_conv_data),
        .out_valid(s_conv_valid),
        .out_ready(s_conv_ready)
    );

    reduce #(
        .WIDTH(P_OUT * B_BIT),
        .N    (P_OCH / P_OUT)
    ) u_reduce (
        .clk      (clk),
        .rst_n    (rst_n),
        .in_valid (s_conv_valid),
        .in_ready (s_conv_ready),
        .in_data  (s_conv_data),
        .out_valid(s_actv_in_valid),
        .out_ready(s_actv_in_ready),
        .out_data (s_actv_in_data)
    );

    activation #(
        .P_CH     (P_OUT),
        .N_CH     (N_OCH),
        .B_BIT    (B_BIT),
        .M_BIT    (M_BIT),
        .A_BIT    (OA_BIT),
        .B_FILE(B_FILE),
        .M_FILE(M_FILE)
    ) u_activation (
        .clk      (clk),
        .rst_n    (rst_n),
        .in_valid (s_actv_in_valid),
        .in_ready (s_actv_in_ready),
        .in_data  (s_actv_in_data),
        .out_valid(out_valid),
        .out_ready(out_ready),
        .out_data (out_data)
    );

endmodule
